using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using System.Reflection;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace TestMonkey.Core
{
	public class TestBuilder
	{
        readonly Setup m_Setup;

		public TestBuilder() {
            m_Setup = new Setup();
            m_Templates.CollectionChanged += Templates_CollectionChanged;
        }

		public TestBuilder(Setup setup) {
            m_Setup = setup;
            m_Templates.CollectionChanged += Templates_CollectionChanged;
        }

        readonly ObservableCollection<TemplateProcessor> m_Templates = new ObservableCollection<TemplateProcessor>();


        void Templates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
                foreach (TemplateProcessor item in e.NewItems)
                    item.Initialize(this);
        }

		public void Run()
		{
			var targetAssembly = Assembly.ReflectionOnlyLoadFrom(Setup.SourceDll);

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_AssemblyResolve;
			
            foreach (var type in targetAssembly.GetTypes())
			{
                if (type.Name.StartsWith("<>"))
                    continue;

                Console.WriteLine("Processing " + type.FullName);
				ProcessType(type);
			}
		}

		Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
		{
			var result = CurrentDomain_AssemblyResolve(args, new DirectoryInfo(Path.GetDirectoryName(args.RequestingAssembly.Location)));
			if (result != null)
				return result;

			foreach (var path in Setup.SearchPaths)
			{
				result = CurrentDomain_AssemblyResolve(args, new DirectoryInfo(path));
				if (result != null)
					return result;
			}

			return null;
		}


        private static Assembly CurrentDomain_AssemblyResolve(ResolveEventArgs args, DirectoryInfo searchPath)
        {
            foreach (var file in searchPath.GetFiles("*.dll"))
            {
                string name= Path.GetFileNameWithoutExtension(file.Name);
                if (file.Name.ToLowerInvariant().EndsWith(".dll") && args.Name.StartsWith(name + ',', StringComparison.InvariantCultureIgnoreCase))
                    return Assembly.ReflectionOnlyLoadFrom(file.FullName);
            }

            return null;
        }

		private void ProcessType(Type type)
		{
            foreach (var template in Templates)
            {
				template.Initialize(this);
                if (template.ShouldRun(type))
                {
                    template.ProcessTemplate(type);
                }
            }
		}


        public Setup Setup
        {
            get { return m_Setup; }
        }

        public ObservableCollection<TemplateProcessor> Templates
        {
            get { return m_Templates; }
        }
    }
}
